library(Seurat)
Attaching SeuratObject
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(ggplot2)
library(stringr)
library(tibble)
library(patchwork)
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

DE table

First we load the sister pair DE tables and filter for:

DE_list <- readRDS("~/spinal_cord_paper/data/Gg_ctrl_poly_sis_markers.rds")

for (i in seq(DE_list)) {
    DE_list[[i]] <- DE_list[[i]] %>% 
    arrange(desc(avg_log2FC)) %>% 
    filter(abs(avg_log2FC) > 0.5) %>% 
    filter(p_val_adj < 0.01)
}

DE_table <- do.call(rbind, DE_list)
dim(DE_table)
[1] 807   8

delta pct distribution

par(mfrow = c(2,2))
hist(abs(DE_list[[1]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[2]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[4]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")
hist(abs(DE_list[[5]]$delta_pct), breaks = 20)
abline(v = 0.1, lty = "dashed", col = "red")

Now we filter the DE lists for absolute delta percentage > 0.1.

for (i in seq(DE_list)) {
  DE_list[[i]] <- DE_list[[i]] %>% 
  filter(abs(delta_pct) > 0.1)
}

DE_table <- do.call(rbind, DE_list)
dim(DE_table)
[1] 671   8

Broad clusters

broad_order <- c("progenitors",
      "FP",
      "RP",
      "FP/RP",
      "neurons",
      "OPC",
      "MFOL",
      "pericytes",
      "microglia",
      "blood",
      "vasculature"
      )

Integrated data

Load the integrated control and poly data.

int_path <- "Gg_ctrl_poly_int_seurat_250723"

my.se <- readRDS(paste0("~/spinal_cord_paper/data/", int_path, ".rds"))
  annot_int <- read.csv(list.files("~/spinal_cord_paper/annotations",
                               pattern = str_remove(int_path, "_seurat_\\d{6}"),
                               full.names = TRUE))
  
  if(length(table(annot_int$number)) != length(table(my.se$seurat_clusters))) {
     stop("Number of clusters must be identical!")
  }
  
  # rename for left join
  annot_int <- annot_int %>% 
    mutate(fine = paste(fine, number, sep = "_")) %>% 
    mutate(number = factor(number, levels = 1:nrow(annot_int))) %>% 
    rename(seurat_clusters = number)
  
  ord_levels <- annot_int$fine[order(match(annot_int$broad, broad_order))]
   
  # add cluster annotation to meta data
  my.se@meta.data <- my.se@meta.data %>% 
    rownames_to_column("rowname") %>% 
    left_join(annot_int, by = "seurat_clusters") %>% 
    mutate(fine = factor(fine, levels = ord_levels)) %>% 
    mutate(seurat_clusters = factor(seurat_clusters, levels = str_extract(ord_levels, "\\d{1,2}$"))) %>% 
    column_to_rownames("rowname")
  
  ctrl_poly_int_combined_labels <- readRDS("~/spinal_cord_paper/annotations/ctrl_poly_int_combined_labels.rds")
  
  my.se <- AddMetaData(my.se, ctrl_poly_int_combined_labels)
  

DimPlot

DimPlot(
  my.se,
  group.by = "annot_sample",
  reduction = "tsne",
  label = TRUE,
  repel = TRUE
  ) +
  NoLegend()

Cluster order

Get the cluster order from the spearman correlation heatmap of the control and poly integrated data. Then we filter for the neuronal clusters only.

corr_heatmap <- readRDS("~/spinal_cord_paper/output/heatmap_spearman_ctrl_poly.rds")

#heatmap order
htmp_order <- data.frame("label" = corr_heatmap[["gtable"]]$grobs[[4]]$label) %>% 
  mutate(label = str_remove(label, "_int")) %>% 
  mutate(label_ordered = paste(str_sub(label,6 ,-1), str_sub(label, 1, 4), sep = "_"))

my.se@meta.data <- my.se@meta.data %>%
  mutate(annot_sample = factor(annot_sample, levels = htmp_order$label_ordered))

Idents(my.se) <- "annot_sample"

# filter for the neuronal clusters
my.se <- subset(my.se, idents = htmp_order$label_ordered[grepl("neurons|MN|CSF", htmp_order$label_ordered)])

DimPlot(
  my.se,
  group.by = "annot_sample",
  reduction = "tsne",
  label = TRUE,
  repel = TRUE
  ) +
  NoLegend()


my.se@active.assay <- "RNA"

Dotplot


# Dotplot of sister pair makrers
pl_all <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample", 
                      # reverse order of unique genes so number one is on top
                    features = rev(unique(DE_table$Gene.stable.ID)),
                    gnames = modplots::gnames,
          cols = c("lightgrey", "black")) +
    theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=0.5)) +
    coord_flip()

pl_all

pdf("~/spinal_cord_paper/figures/Sister_pair_DE_dotplot.pdf", width = 15, height = 32)
  pl_all


  
DE_table$Gene.name[duplicated(DE_table$Gene.stable.ID)]
 [1] "HES5"               "MAP6"               "GNG5"               "ST18"               "GAD1"               "FABP3"             
 [7] "SYT1"               "SLC32A1"            "KIF5C"              "HMP19"              "GALNT9"             "VSTM2L"            
[13] "HINTW"              "DNER"               "CRABP-I"            "RELN"               "PAX2"               "NEUROD2"           
[19] "CHL1"               "LHX1"               "NRXN3"              "ENSGALG00000029521" "BHLHE22"            "SPOCK1"            
[25] "SSTR1"              "SLC32A1"            "NCALD"              "ID2"                "GRIK3"              "GAD2"              
[31] "PTPRK"              "GABRG3"             "GAD1"               "RUNX1T1"            "HPCAL1"             "ZEB2"              
[37] "GALNT9"             "ENSGALG00000013212" "MDK"                "ZFPM2"              "RELN"               "NEUROD6"           
[43] "CPLX1"              "LAMP5"              "WNT5A"              "HINTW"              "SOX4"               "DKK3"              
[49] "UNC13B"             "ATP1B1"             "GALNT17"            "RASD1"              "ENSGALG00000051980" "PLXNA4"            
[55] "DACT2"              "DISP3"              "MVB12B"             "ENSGALG00000054223" "CNTN4"              "ZNF423"            
[61] "CBLB"               "FKBP1B"             "CELF2"              "EPB41L4A"           "PXYLP1"             "ENSGALG00000023640"
[67] "CNTN2"              "MRPS6"              "PPP3CA"             "NFIX"               "NFIA"               "SOX8"              
[73] "DRAXIN"             "CRABP-I"            "NHLH1"              "TAC1"               "VSTM2L"             "CPNE2"             
[79] "PRKCA"             

Individual dot plots


# select top50 by log2FC 
for (i in seq(DE_list)) {
    DE_list[[i]] <- DE_list[[i]] %>%
    slice_max(order_by = abs(avg_log2FC), n = 50) %>% 
    arrange(desc(avg_log2FC))
}

p1 <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample", 
                    assay = "RNA",
                      # reverse order of DE genes so number one is on top
                    features = rev(DE_list[[1]]$Gene.stable.ID),
                    gnames = modplots::gnames,
          cols = c("lightgrey", "black")) +
    theme(axis.text.x = element_blank()) +
    coord_flip() +
    xlab(names(DE_list)[1]) +
    ylab(element_blank())

p2 <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample",  
                    assay = "RNA",
                      # reverse order of DE genes so number one is on top
                    features = rev(DE_list[[2]]$Gene.stable.ID),
                    gnames = modplots::gnames,
          cols = c("lightgrey", "black")) +
    theme(axis.text.x = element_blank()) +
    coord_flip() +
    xlab(names(DE_list)[2]) +
    ylab(element_blank())

p3 <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample",  
                    assay = "RNA",
                      # reverse order of DE genes so number one is on top
                    features = rev(DE_list[[5]]$Gene.stable.ID),
                    gnames = modplots::gnames,
          cols = c("lightgrey", "black")) +
    theme(axis.text.x = element_blank()) +
    coord_flip() +
    xlab(names(DE_list)[5]) +
    ylab(element_blank())

p4 <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample",  
                    assay = "RNA",
                      # reverse order of DE genes so number one is on top
                    features = rev(DE_list[[4]]$Gene.stable.ID),
                    gnames = modplots::gnames,
          cols = c("lightgrey", "black")) +
    theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=0.5)) +
    coord_flip() +
    xlab(names(DE_list)[4]) +
    ylab(element_blank())
layout <- "CCDD
           CC##"

pdf("~/spinal_cord_paper/figures/Supp_Fig_5_ctrl_poly_dotplot_individual.pdf", height = 21, width = 7)
# without labels for proper alignment
(p1 + p2 + plot_layout(guides = "collect")) /
(p3 + p4 + plot_layout(guides = "collect", design = layout)) & 
  theme(axis.text.x = element_blank(),
        axis.text.y = element_blank())
# with labels to transfer in illustrator
(p1 + p2 + plot_layout(guides = "collect")) /
(p3 + p4 + plot_layout(guides = "collect", design = layout))

dev.off()
null device 
          1 

Volcanoplots

p.adj <- 0.01
l2fc <- 0

# select top50 by log2FC 
for (i in seq(DE_list)) {
    DE_list[[i]] <- DE_list[[i]] %>% 
    mutate(delta_pct_sign = case_when(
      delta_pct < 0 ~ "-",
      delta_pct > 0 ~ "+",
      delta_pct == 0 ~ "0"
    ))
}
 

toplot <- do.call(rbind, DE_list[c(4,1)]) %>% 
  rownames_to_column("contrast") %>% 
  mutate(contrast = str_remove(contrast, "\\.\\d{1,2}")) %>% 
  mutate(contrast = str_replace_all(contrast, " ", "_"))

volplot <- ggplot(data = toplot,
       aes(x = avg_log2FC,
           y = -log10(p_val_adj),
           label = Gene.name,
           color = delta_pct_sign,
           size = abs(delta_pct)
       )) +
  geom_point(shape = 21) +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = c("red", "black")) +
  scale_size_continuous(range = c(0.5, 4)) +
  facet_wrap("contrast", ncol = 1, scales = "free_y") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot)
NA
pdf("~/spinal_cord_paper/figures/Fig_5_volcanoplots.pdf", width = 7, height = 10)
(volplot +
  ggrepel::geom_text_repel(size = 3, color = "black"))
Warning: ggrepel: 10 unlabeled data points (too many overlaps). Consider increasing max.overlaps

Specific markers

Find Markers for clusters 11_ctrl, 16_ctrl, and 15_poly.

gnames <- modplots::gnames

markers <- list()

clu <- c("inhibitory_neurons_16_ctrl",
         "excitatory neurons_11_ctrl",
         "excitatory_neurons_15_poly")

for (i in seq(clu)) {  
  markers[[i]] <- FindMarkers(
      my.se,
      ident.1 = clu[i],
      group.by = "annot_sample",
      assay = "RNA",
      verbose = FALSE,
      only.pos = TRUE, # we look for overexpressed, specific markers
      min.pct = 0.25,
      logfc.threshold =  0.25,
      latent.vars = c("CC.Difference.seurat"),
      test.use = "MAST"
    ) %>%
      tibble::rownames_to_column("Gene.stable.ID") %>%
      dplyr::left_join(gnames, by = "Gene.stable.ID") %>%
      dplyr::arrange(-avg_log2FC) %>%
      dplyr::filter(p_val_adj < 0.05) %>%
      dplyr::filter(abs(avg_log2FC) > 0.5) %>%
    dplyr::mutate(delta_pct = abs(pct.1 - pct.2))
}

names(markers) <- clu

Specific marker dotplot

Plot the top 50 markers for clusters 11_ctrl, 16_ctrl, and 15_poly.

n <- 50

mark_plot <- list()

for (i in seq(clu)) {
  mark_plot[[i]] <- modplots::mDotPlot2(my.se,
                    group.by = "annot_sample", 
                      # reverse order of markers so number one is on top
                    features = rev(markers[[i]][1:n,"Gene.stable.ID"]),
                    gnames = modplots::gnames) +
    theme(axis.text.x = element_text(angle = 90, hjust=1, vjust=0.5)) +
    coord_flip() +
  scale_colour_gradientn(colours = c("gray90","gray80","yellow", "orange", "red", "darkred", "darkred")) +
  ggtitle(paste0("Top ", n, " markers by log2FC for ", clu[i]))

}

mark_plot[[1]]
mark_plot[[2]]
mark_plot[[3]]


pdf("~/spinal_cord_paper/figures/Sister_pair_neuron_marker_dotplots.pdf", width = 14, height = n/3)
mark_plot[[1]]
mark_plot[[2]]
mark_plot[[3]]
# Date and time of Rendering
Sys.time()

sessionInfo()
LS0tCnRpdGxlOiAiU2lzdGVyIHBhaXIgREUgaGVhdG1hcCBjdHJsIHBvbHkiCmF1dGhvcjogIkZhYmlvIFNhY2hlciIKZGF0ZTogIjE4LjA5LjIwMjMiCmRhdGE6Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogIGh0bWxfbm90ZWJvb2s6CiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDgKZWRpdG9yX29wdGlvbnM6CiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIGxpYnJhcmllc30KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkocGxvdGx5KQpgYGAKCiMgREUgdGFibGUKCkZpcnN0IHdlIGxvYWQgdGhlIHNpc3RlciBwYWlyIERFIHRhYmxlcyBhbmQgZmlsdGVyIGZvcjoKCi0gICBhYnNvbHV0ZSBhdmdfbG9nMkZDIFw+IDAuNSAoXH40MSUgaW5jcmVhc2UpCgotICAgcF92YWxfYWRqIFw8IDAuMDEKCmBgYHtyIERFLWRhdGF9CkRFX2xpc3QgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfcG9seV9zaXNfbWFya2Vycy5yZHMiKQoKZm9yIChpIGluIHNlcShERV9saXN0KSkgewogICAgREVfbGlzdFtbaV1dIDwtIERFX2xpc3RbW2ldXSAlPiUgCiAgICBhcnJhbmdlKGRlc2MoYXZnX2xvZzJGQykpICU+JSAKICAgIGZpbHRlcihhYnMoYXZnX2xvZzJGQykgPiAwLjUpICU+JSAKICAgIGZpbHRlcihwX3ZhbF9hZGogPCAwLjAxKQp9CgpERV90YWJsZSA8LSBkby5jYWxsKHJiaW5kLCBERV9saXN0KQpkaW0oREVfdGFibGUpCmBgYAoKIyMgZGVsdGEgcGN0IGRpc3RyaWJ1dGlvbgoKYGBge3IgZGVsdGEtcGN0LWhpc3RvZ3JhbXN9CnBhcihtZnJvdyA9IGMoMiwyKSkKaGlzdChhYnMoREVfbGlzdFtbMV1dJGRlbHRhX3BjdCksIGJyZWFrcyA9IDIwKQphYmxpbmUodiA9IDAuMSwgbHR5ID0gImRhc2hlZCIsIGNvbCA9ICJyZWQiKQpoaXN0KGFicyhERV9saXN0W1syXV0kZGVsdGFfcGN0KSwgYnJlYWtzID0gMjApCmFibGluZSh2ID0gMC4xLCBsdHkgPSAiZGFzaGVkIiwgY29sID0gInJlZCIpCmhpc3QoYWJzKERFX2xpc3RbWzRdXSRkZWx0YV9wY3QpLCBicmVha3MgPSAyMCkKYWJsaW5lKHYgPSAwLjEsIGx0eSA9ICJkYXNoZWQiLCBjb2wgPSAicmVkIikKaGlzdChhYnMoREVfbGlzdFtbNV1dJGRlbHRhX3BjdCksIGJyZWFrcyA9IDIwKQphYmxpbmUodiA9IDAuMSwgbHR5ID0gImRhc2hlZCIsIGNvbCA9ICJyZWQiKQpgYGAKCk5vdyB3ZSBmaWx0ZXIgdGhlIERFIGxpc3RzIGZvciBhYnNvbHV0ZSBkZWx0YSBwZXJjZW50YWdlIFw+IDAuMS4KCmBgYHtyIGZpbHRlci1kZWx0YS1wY3R9CmZvciAoaSBpbiBzZXEoREVfbGlzdCkpIHsKICBERV9saXN0W1tpXV0gPC0gREVfbGlzdFtbaV1dICU+JSAKICBmaWx0ZXIoYWJzKGRlbHRhX3BjdCkgPiAwLjEpCn0KCkRFX3RhYmxlIDwtIGRvLmNhbGwocmJpbmQsIERFX2xpc3QpCmRpbShERV90YWJsZSkKYGBgCgojIEJyb2FkIGNsdXN0ZXJzCgpgYGB7ciBjbHVzdGVyLW9yZGVyfQpicm9hZF9vcmRlciA8LSBjKCJwcm9nZW5pdG9ycyIsCiAgICAgICJGUCIsCiAgICAgICJSUCIsCiAgICAgICJGUC9SUCIsCiAgICAgICJuZXVyb25zIiwKICAgICAgIk9QQyIsCiAgICAgICJNRk9MIiwKICAgICAgInBlcmljeXRlcyIsCiAgICAgICJtaWNyb2dsaWEiLAogICAgICAiYmxvb2QiLAogICAgICAidmFzY3VsYXR1cmUiCiAgICAgICkKCmBgYAoKIyBJbnRlZ3JhdGVkIGRhdGEKCkxvYWQgdGhlIGludGVncmF0ZWQgY29udHJvbCBhbmQgcG9seSBkYXRhLgoKYGBge3IgaW50ZWdyYXRlZC1kYXRhLXBvbHl9CmludF9wYXRoIDwtICJHZ19jdHJsX3BvbHlfaW50X3NldXJhdF8yNTA3MjMiCgpteS5zZSA8LSByZWFkUkRTKHBhc3RlMCgifi9zcGluYWxfY29yZF9wYXBlci9kYXRhLyIsIGludF9wYXRoLCAiLnJkcyIpKQogIGFubm90X2ludCA8LSByZWFkLmNzdihsaXN0LmZpbGVzKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2Fubm90YXRpb25zIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBzdHJfcmVtb3ZlKGludF9wYXRoLCAiX3NldXJhdF9cXGR7Nn0iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUUlVFKSkKICAKICBpZihsZW5ndGgodGFibGUoYW5ub3RfaW50JG51bWJlcikpICE9IGxlbmd0aCh0YWJsZShteS5zZSRzZXVyYXRfY2x1c3RlcnMpKSkgewogICAgIHN0b3AoIk51bWJlciBvZiBjbHVzdGVycyBtdXN0IGJlIGlkZW50aWNhbCEiKQogIH0KICAKICAjIHJlbmFtZSBmb3IgbGVmdCBqb2luCiAgYW5ub3RfaW50IDwtIGFubm90X2ludCAlPiUgCiAgICBtdXRhdGUoZmluZSA9IHBhc3RlKGZpbmUsIG51bWJlciwgc2VwID0gIl8iKSkgJT4lIAogICAgbXV0YXRlKG51bWJlciA9IGZhY3RvcihudW1iZXIsIGxldmVscyA9IDE6bnJvdyhhbm5vdF9pbnQpKSkgJT4lIAogICAgcmVuYW1lKHNldXJhdF9jbHVzdGVycyA9IG51bWJlcikKICAKICBvcmRfbGV2ZWxzIDwtIGFubm90X2ludCRmaW5lW29yZGVyKG1hdGNoKGFubm90X2ludCRicm9hZCwgYnJvYWRfb3JkZXIpKV0KICAgCiAgIyBhZGQgY2x1c3RlciBhbm5vdGF0aW9uIHRvIG1ldGEgZGF0YQogIG15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lIAogICAgcm93bmFtZXNfdG9fY29sdW1uKCJyb3duYW1lIikgJT4lIAogICAgbGVmdF9qb2luKGFubm90X2ludCwgYnkgPSAic2V1cmF0X2NsdXN0ZXJzIikgJT4lIAogICAgbXV0YXRlKGZpbmUgPSBmYWN0b3IoZmluZSwgbGV2ZWxzID0gb3JkX2xldmVscykpICU+JSAKICAgIG11dGF0ZShzZXVyYXRfY2x1c3RlcnMgPSBmYWN0b3Ioc2V1cmF0X2NsdXN0ZXJzLCBsZXZlbHMgPSBzdHJfZXh0cmFjdChvcmRfbGV2ZWxzLCAiXFxkezEsMn0kIikpKSAlPiUgCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInJvd25hbWUiKQogIAogIGN0cmxfcG9seV9pbnRfY29tYmluZWRfbGFiZWxzIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvY3RybF9wb2x5X2ludF9jb21iaW5lZF9sYWJlbHMucmRzIikKICAKICBteS5zZSA8LSBBZGRNZXRhRGF0YShteS5zZSwgY3RybF9wb2x5X2ludF9jb21iaW5lZF9sYWJlbHMpCiAgCmBgYAoKIyBEaW1QbG90CgpgYGB7ciBkaW1wbG90fQpEaW1QbG90KAogIG15LnNlLAogIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUKICApICsKICBOb0xlZ2VuZCgpCgpgYGAKCiMgQ2x1c3RlciBvcmRlcgoKR2V0IHRoZSBjbHVzdGVyIG9yZGVyIGZyb20gdGhlIHNwZWFybWFuIGNvcnJlbGF0aW9uIGhlYXRtYXAgb2YgdGhlIGNvbnRyb2wgYW5kIHBvbHkgaW50ZWdyYXRlZCBkYXRhLiBUaGVuIHdlIGZpbHRlciBmb3IgdGhlIG5ldXJvbmFsIGNsdXN0ZXJzIG9ubHkuCgpgYGB7ciBmYWN0b3Itb3JkZXJ9CmNvcnJfaGVhdG1hcCA8LSByZWFkUkRTKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL291dHB1dC9oZWF0bWFwX3NwZWFybWFuX2N0cmxfcG9seS5yZHMiKQoKI2hlYXRtYXAgb3JkZXIKaHRtcF9vcmRlciA8LSBkYXRhLmZyYW1lKCJsYWJlbCIgPSBjb3JyX2hlYXRtYXBbWyJndGFibGUiXV0kZ3JvYnNbWzRdXSRsYWJlbCkgJT4lIAogIG11dGF0ZShsYWJlbCA9IHN0cl9yZW1vdmUobGFiZWwsICJfaW50IikpICU+JSAKICBtdXRhdGUobGFiZWxfb3JkZXJlZCA9IHBhc3RlKHN0cl9zdWIobGFiZWwsNiAsLTEpLCBzdHJfc3ViKGxhYmVsLCAxLCA0KSwgc2VwID0gIl8iKSkKCm15LnNlQG1ldGEuZGF0YSA8LSBteS5zZUBtZXRhLmRhdGEgJT4lCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IGZhY3Rvcihhbm5vdF9zYW1wbGUsIGxldmVscyA9IGh0bXBfb3JkZXIkbGFiZWxfb3JkZXJlZCkpCgpJZGVudHMobXkuc2UpIDwtICJhbm5vdF9zYW1wbGUiCgojIGZpbHRlciBmb3IgdGhlIG5ldXJvbmFsIGNsdXN0ZXJzCm15LnNlIDwtIHN1YnNldChteS5zZSwgaWRlbnRzID0gaHRtcF9vcmRlciRsYWJlbF9vcmRlcmVkW2dyZXBsKCJuZXVyb25zfE1OfENTRiIsIGh0bXBfb3JkZXIkbGFiZWxfb3JkZXJlZCldKQoKRGltUGxvdCgKICBteS5zZSwKICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLAogIHJlZHVjdGlvbiA9ICJ0c25lIiwKICBsYWJlbCA9IFRSVUUsCiAgcmVwZWwgPSBUUlVFCiAgKSArCiAgTm9MZWdlbmQoKQoKbXkuc2VAYWN0aXZlLmFzc2F5IDwtICJSTkEiCgpgYGAKCiMgRG90cGxvdAoKYGBge3IgYWxsX0RFX2RvdHBsb3QsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0zMH0KCiMgRG90cGxvdCBvZiBzaXN0ZXIgcGFpciBtYWtyZXJzCnBsX2FsbCA8LSBtb2RwbG90czo6bURvdFBsb3QyKG15LnNlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsIAogICAgICAgICAgICAgICAgICAgICAgIyByZXZlcnNlIG9yZGVyIG9mIHVuaXF1ZSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KHVuaXF1ZShERV90YWJsZSRHZW5lLnN0YWJsZS5JRCkpLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMsCiAgICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImJsYWNrIikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0PTEsIHZqdXN0PTAuNSkpICsKICAgIGNvb3JkX2ZsaXAoKQoKcGxfYWxsCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9TaXN0ZXJfcGFpcl9ERV9kb3RwbG90LnBkZiIsIHdpZHRoID0gMTUsIGhlaWdodCA9IDMyKQogIHBsX2FsbAoKICAKREVfdGFibGUkR2VuZS5uYW1lW2R1cGxpY2F0ZWQoREVfdGFibGUkR2VuZS5zdGFibGUuSUQpXQpgYGAKCiMgSW5kaXZpZHVhbCBkb3QgcGxvdHMKCmBgYHtyIGluZGl2aWR1YWxfREVfZG90cGxvdH0KCiMgc2VsZWN0IHRvcDUwIGJ5IGxvZzJGQyAKZm9yIChpIGluIHNlcShERV9saXN0KSkgewogICAgREVfbGlzdFtbaV1dIDwtIERFX2xpc3RbW2ldXSAlPiUKICAgIHNsaWNlX21heChvcmRlcl9ieSA9IGFicyhhdmdfbG9nMkZDKSwgbiA9IDUwKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MoYXZnX2xvZzJGQykpCn0KCnAxIDwtIG1vZHBsb3RzOjptRG90UGxvdDIobXkuc2UsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3Rfc2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICMgcmV2ZXJzZSBvcmRlciBvZiBERSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KERFX2xpc3RbWzFdXSRHZW5lLnN0YWJsZS5JRCksCiAgICAgICAgICAgICAgICAgICAgZ25hbWVzID0gbW9kcGxvdHM6OmduYW1lcywKICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGdyZXkiLCAiYmxhY2siKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICB4bGFiKG5hbWVzKERFX2xpc3QpWzFdKSArCiAgICB5bGFiKGVsZW1lbnRfYmxhbmsoKSkKCnAyIDwtIG1vZHBsb3RzOjptRG90UGxvdDIobXkuc2UsCiAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiYW5ub3Rfc2FtcGxlIiwgIAogICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIsCiAgICAgICAgICAgICAgICAgICAgICAjIHJldmVyc2Ugb3JkZXIgb2YgREUgZ2VuZXMgc28gbnVtYmVyIG9uZSBpcyBvbiB0b3AKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IHJldihERV9saXN0W1syXV0kR2VuZS5zdGFibGUuSUQpLAogICAgICAgICAgICAgICAgICAgIGduYW1lcyA9IG1vZHBsb3RzOjpnbmFtZXMsCiAgICAgICAgICBjb2xzID0gYygibGlnaHRncmV5IiwgImJsYWNrIikpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgeGxhYihuYW1lcyhERV9saXN0KVsyXSkgKwogICAgeWxhYihlbGVtZW50X2JsYW5rKCkpCgpwMyA8LSBtb2RwbG90czo6bURvdFBsb3QyKG15LnNlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsICAKICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgIyByZXZlcnNlIG9yZGVyIG9mIERFIGdlbmVzIHNvIG51bWJlciBvbmUgaXMgb24gdG9wCiAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSByZXYoREVfbGlzdFtbNV1dJEdlbmUuc3RhYmxlLklEKSwKICAgICAgICAgICAgICAgICAgICBnbmFtZXMgPSBtb2RwbG90czo6Z25hbWVzLAogICAgICAgICAgY29scyA9IGMoImxpZ2h0Z3JleSIsICJibGFjayIpKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHhsYWIobmFtZXMoREVfbGlzdClbNV0pICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKQoKcDQgPC0gbW9kcGxvdHM6Om1Eb3RQbG90MihteS5zZSwKICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLCAgCiAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICMgcmV2ZXJzZSBvcmRlciBvZiBERSBnZW5lcyBzbyBudW1iZXIgb25lIGlzIG9uIHRvcAogICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gcmV2KERFX2xpc3RbWzRdXSRHZW5lLnN0YWJsZS5JRCksCiAgICAgICAgICAgICAgICAgICAgZ25hbWVzID0gbW9kcGxvdHM6OmduYW1lcywKICAgICAgICAgIGNvbHMgPSBjKCJsaWdodGdyZXkiLCAiYmxhY2siKSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3Q9MSwgdmp1c3Q9MC41KSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHhsYWIobmFtZXMoREVfbGlzdClbNF0pICsKICAgIHlsYWIoZWxlbWVudF9ibGFuaygpKQoKYGBgCgpgYGB7ciBleHBvcnRfcGxvdHMgfQpsYXlvdXQgPC0gIkNDREQKICAgICAgICAgICBDQyMjIgoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvU3VwcF9GaWdfNV9jdHJsX3BvbHlfZG90cGxvdF9pbmRpdmlkdWFsLnBkZiIsIGhlaWdodCA9IDIxLCB3aWR0aCA9IDcpCiMgd2l0aG91dCBsYWJlbHMgZm9yIHByb3BlciBhbGlnbm1lbnQKKHAxICsgcDIgKyBwbG90X2xheW91dChndWlkZXMgPSAiY29sbGVjdCIpKSAvCihwMyArIHA0ICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiLCBkZXNpZ24gPSBsYXlvdXQpKSAmIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQojIHdpdGggbGFiZWxzIHRvIHRyYW5zZmVyIGluIGlsbHVzdHJhdG9yCihwMSArIHAyICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSkgLwoocDMgKyBwNCArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IiwgZGVzaWduID0gbGF5b3V0KSkKCmRldi5vZmYoKQpgYGAKCiMgVm9sY2Fub3Bsb3RzCgpgYGB7ciB2b2xjYW5vcGxvdHMsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD03fQpwLmFkaiA8LSAwLjAxCmwyZmMgPC0gMAoKIyBzZWxlY3QgdG9wNTAgYnkgbG9nMkZDIApmb3IgKGkgaW4gc2VxKERFX2xpc3QpKSB7CiAgICBERV9saXN0W1tpXV0gPC0gREVfbGlzdFtbaV1dICU+JSAKICAgIG11dGF0ZShkZWx0YV9wY3Rfc2lnbiA9IGNhc2Vfd2hlbigKICAgICAgZGVsdGFfcGN0IDwgMCB+ICItIiwKICAgICAgZGVsdGFfcGN0ID4gMCB+ICIrIiwKICAgICAgZGVsdGFfcGN0ID09IDAgfiAiMCIKICAgICkpCn0KIAoKdG9wbG90IDwtIGRvLmNhbGwocmJpbmQsIERFX2xpc3RbYyg0LDEpXSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiY29udHJhc3QiKSAlPiUgCiAgbXV0YXRlKGNvbnRyYXN0ID0gc3RyX3JlbW92ZShjb250cmFzdCwgIlxcLlxcZHsxLDJ9IikpICU+JSAKICBtdXRhdGUoY29udHJhc3QgPSBzdHJfcmVwbGFjZV9hbGwoY29udHJhc3QsICIgIiwgIl8iKSkKCnZvbHBsb3QgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3QsCiAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgIGxhYmVsID0gR2VuZS5uYW1lLAogICAgICAgICAgIGNvbG9yID0gZGVsdGFfcGN0X3NpZ24sCiAgICAgICAgICAgc2l6ZSA9IGFicyhkZWx0YV9wY3QpCiAgICAgICApKSArCiAgZ2VvbV9wb2ludChzaGFwZSA9IDIxKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiZ29sZGVucm9kMyIsICJibGFjayIpKSArCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlID0gYygwLjUsIDQpKSArCiAgZmFjZXRfd3JhcCgiY29udHJhc3QiLCBuY29sID0gMSwgc2NhbGVzID0gImZyZWVfeSIpICsKICB5bGFiKCItbG9nMTAocGFkaikiKSArCiAgdGhlbWVfYncoKQoKZ2dwbG90bHkodm9scGxvdCkKCmBgYAoKYGBge3J9CnBkZigifi9zcGluYWxfY29yZF9wYXBlci9maWd1cmVzL0ZpZ181X3ZvbGNhbm9wbG90cy5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDEwKQoodm9scGxvdCArCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKHNpemUgPSAzLCBjb2xvciA9ICJibGFjayIpKQoKYGBgCiAKIyBTcGVjaWZpYyBtYXJrZXJzCgpGaW5kIE1hcmtlcnMgZm9yIGNsdXN0ZXJzIDExX2N0cmwsIDE2X2N0cmwsIGFuZCAxNV9wb2x5LgoKYGBge3Igc3BlY2lmaWMtbWFya2Vyc30KZ25hbWVzIDwtIG1vZHBsb3RzOjpnbmFtZXMKCm1hcmtlcnMgPC0gbGlzdCgpCgpjbHUgPC0gYygiaW5oaWJpdG9yeV9uZXVyb25zXzE2X2N0cmwiLAogICAgICAgICAiZXhjaXRhdG9yeSBuZXVyb25zXzExX2N0cmwiLAogICAgICAgICAiZXhjaXRhdG9yeV9uZXVyb25zXzE1X3BvbHkiKQoKZm9yIChpIGluIHNlcShjbHUpKSB7ICAKICBtYXJrZXJzW1tpXV0gPC0gRmluZE1hcmtlcnMoCiAgICAgIG15LnNlLAogICAgICBpZGVudC4xID0gY2x1W2ldLAogICAgICBncm91cC5ieSA9ICJhbm5vdF9zYW1wbGUiLAogICAgICBhc3NheSA9ICJSTkEiLAogICAgICB2ZXJib3NlID0gRkFMU0UsCiAgICAgIG9ubHkucG9zID0gVFJVRSwgIyB3ZSBsb29rIGZvciBvdmVyZXhwcmVzc2VkLCBzcGVjaWZpYyBtYXJrZXJzCiAgICAgIG1pbi5wY3QgPSAwLjI1LAogICAgICBsb2dmYy50aHJlc2hvbGQgPSAgMC4yNSwKICAgICAgbGF0ZW50LnZhcnMgPSBjKCJDQy5EaWZmZXJlbmNlLnNldXJhdCIpLAogICAgICB0ZXN0LnVzZSA9ICJNQVNUIgogICAgKSAlPiUKICAgICAgdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oIkdlbmUuc3RhYmxlLklEIikgJT4lCiAgICAgIGRwbHlyOjpsZWZ0X2pvaW4oZ25hbWVzLCBieSA9ICJHZW5lLnN0YWJsZS5JRCIpICU+JQogICAgICBkcGx5cjo6YXJyYW5nZSgtYXZnX2xvZzJGQykgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgICAgIGRwbHlyOjpmaWx0ZXIoYWJzKGF2Z19sb2cyRkMpID4gMC41KSAlPiUKICAgIGRwbHlyOjptdXRhdGUoZGVsdGFfcGN0ID0gYWJzKHBjdC4xIC0gcGN0LjIpKQp9CgpuYW1lcyhtYXJrZXJzKSA8LSBjbHUKYGBgCgojIFNwZWNpZmljIG1hcmtlciBkb3RwbG90CgpQbG90IHRoZSB0b3AgNTAgbWFya2VycyBmb3IgY2x1c3RlcnMgMTFfY3RybCwgMTZfY3RybCwgYW5kIDE1X3BvbHkuCgpgYGB7ciBuZXVyb24tbWFya2VyLWRvdHBsb3RzLCBmaWcud2lkdGg9OSwgZmlnLmhlaWdodD02fQpuIDwtIDUwCgptYXJrX3Bsb3QgPC0gbGlzdCgpCgpmb3IgKGkgaW4gc2VxKGNsdSkpIHsKICBtYXJrX3Bsb3RbW2ldXSA8LSBtb2RwbG90czo6bURvdFBsb3QyKG15LnNlLAogICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImFubm90X3NhbXBsZSIsIAogICAgICAgICAgICAgICAgICAgICAgIyByZXZlcnNlIG9yZGVyIG9mIG1hcmtlcnMgc28gbnVtYmVyIG9uZSBpcyBvbiB0b3AKICAgICAgICAgICAgICAgICAgICBmZWF0dXJlcyA9IHJldihtYXJrZXJzW1tpXV1bMTpuLCJHZW5lLnN0YWJsZS5JRCJdKSwKICAgICAgICAgICAgICAgICAgICBnbmFtZXMgPSBtb2RwbG90czo6Z25hbWVzKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdD0xLCB2anVzdD0wLjUpKSArCiAgICBjb29yZF9mbGlwKCkgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudG4oY29sb3VycyA9IGMoImdyYXk5MCIsImdyYXk4MCIsInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIiwgImRhcmtyZWQiLCAiZGFya3JlZCIpKSArCiAgZ2d0aXRsZShwYXN0ZTAoIlRvcCAiLCBuLCAiIG1hcmtlcnMgYnkgbG9nMkZDIGZvciAiLCBjbHVbaV0pKQoKfQoKbWFya19wbG90W1sxXV0KbWFya19wbG90W1syXV0KbWFya19wbG90W1szXV0KCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9TaXN0ZXJfcGFpcl9uZXVyb25fbWFya2VyX2RvdHBsb3RzLnBkZiIsIHdpZHRoID0gMTQsIGhlaWdodCA9IG4vMykKbWFya19wbG90W1sxXV0KbWFya19wbG90W1syXV0KbWFya19wbG90W1szXV0KCmBgYAoKYGBge3IgU2Vzc2lvbi1pbmZvfQojIERhdGUgYW5kIHRpbWUgb2YgUmVuZGVyaW5nClN5cy50aW1lKCkKCnNlc3Npb25JbmZvKCkKYGBgCg==